﻿using System;
using System.Collections.Concurrent;
using System.Reflection;

using RpcClient.Attr;
using RpcClient.EventBus;
using RpcClient.Interface;

using RpcHelper;

namespace RpcClient.Collect
{
    /// <summary>
    /// 本地事件集合
    /// </summary>
    [Attr.ClassLifetimeAttr(Attr.ClassLifetimeType.单例)]
    internal class LocalEventCollect : ILocalEventCollect
    {
        private static readonly Type _IgnoreType = typeof(IgnoreIoc);
        private static readonly Type _AttrType = typeof(Attr.LocalEventName);
        /// <summary>
        /// 事件列表
        /// </summary>
        private static readonly ConcurrentDictionary<string, LocalEvent> _Events = new ConcurrentDictionary<string, LocalEvent>();
        /// <summary>
        /// 注册事件
        /// </summary>
        /// <typeparam name="T">事件源</typeparam>
        /// <param name="handler">事件处理类</param>
        /// <returns>是否成功</returns>
        public bool RegEvent<T>(IEventHandler<T> handler)
        {
            return this._RegEvent(typeof(IEventHandler<T>), handler.GetType());
        }
        /// <summary>
        /// 注册事件
        /// </summary>
        /// <typeparam name="T">事件源</typeparam>
        /// <param name="action">事件委托</param>
        /// <param name="name">事件名称</param>
        /// <returns>是否成功</returns>
        public bool RegEvent<T>(LocalEvent<T> action, string name)
        {
            return this._RegEvent(action, name);
        }
        /// <summary>
        /// 注册事件
        /// </summary>
        /// <typeparam name="T">事件源</typeparam>
        /// <param name="action">事件委托</param>
        /// <returns>是否成功</returns>
        public bool RegEvent<T>(LocalEvent<T> action)
        {
            return this._RegEvent(action, string.Empty);
        }
        /// <summary>
        /// 获取事件名称
        /// </summary>
        /// <param name="type">事件源类型</param>
        /// <returns>事件名</returns>
        private string _GetEventName(Type type)
        {
            LocalEventName attr = (LocalEventName)type.GetCustomAttribute(_AttrType);
            if (attr == null)
            {
                return null;
            }
            return attr.EventName;
        }
        /// <summary>
        /// 获取事件名称
        /// </summary>
        /// <param name="type">事件源类型</param>
        /// <param name="name">事件名</param>
        /// <returns>事件名</returns>
        private string _GetEventName(Type type, string name)
        {
            if (name == null)
            {
                return type.FullName;
            }
            return string.Concat(type.FullName, "_", name);
        }
        /// <summary>
        /// 移除本地事件
        /// </summary>
        /// <typeparam name="T">事件源</typeparam>
        /// <param name="name">事件名</param>
        public void RemoveEvent<T>(string name = null)
        {
            this._RemoveEvent(typeof(T), name);
        }
        /// <summary>
        /// 移除本地事件
        /// </summary>
        /// <param name="type">事件源</param>
        /// <param name="name">事件名</param>
        private void _RemoveEvent(Type type, string name)
        {
            string key = this._GetEventName(type, name);
            if (_Events.TryGetValue(key, out LocalEvent local))
            {
                local.Remove(type.FullName);
            }
        }
        /// <summary>
        /// 发布
        /// </summary>
        /// <param name="eventData">事件数据</param>
        /// <param name="name">事件名</param>
        public void Public(object eventData, string name = null)
        {
            Type type = eventData.GetType();
            string key = this._GetEventName(type, name);
            if (_Events.TryGetValue(key, out LocalEvent local))
            {
                local.Post(eventData);
            }
        }
        private bool _RegEvent(Type form, Type to)
        {
            string name = this._GetEventName(to);
            Type elem = form.GenericTypeArguments[0];
            LocalEvent local = this._GetLocalEvent(elem, name);
            RpcClient.Unity.RegisterType(form, to, to.FullName);
            return local.Reg(form, to.FullName);
        }
        private bool _RegEvent<T>(LocalEvent<T> action, string name)
        {
            LocalEventDelegate<T> dele = new EventBus.LocalEventDelegate<T>(action);
            Type elem = typeof(T);
            LocalEvent local = this._GetLocalEvent(elem, name);
            Type to = action.GetType();
            RpcClient.Unity.RegisterInstance(dele, to.FullName);
            return local.Reg(typeof(IEventHandler<T>), to.FullName);
        }
        public void RegLocalEvent(Assembly assembly)
        {
            assembly.GetTypes().ForEach(a =>
            {
                if (!a.IsClass || a.GetCustomAttribute(_IgnoreType) != null)
                {
                    return;
                }
                Type[] types = a.GetInterfaces();
                if (types.IsNull())
                {
                    return;
                }
                Type form = types.Find(b => b.Namespace == "RpcClient.Interface" && b.IsGenericType && b.Name == "IEventHandler`1");
                if (form == null)
                {
                    return;
                }
                this._RegEvent(form, a);
            });
        }
        private LocalEvent _GetLocalEvent(Type type, string name)
        {
            string key = this._GetEventName(type, name);
            if (name == null)
            {
                name = type.Name;
            }
            return _Events.GetOrAdd(key, new LocalEvent(name));
        }



        public void AsyncPublic(object eventData, string name = null)
        {
            Type type = eventData.GetType();
            string key = this._GetEventName(type, name);
            if (_Events.TryGetValue(key, out LocalEvent local))
            {
                local.AsyncPost(eventData);
            }
        }
    }
}
